package com.weichen.studentgameexchange.game.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.weichen.studentgameexchange.common.common.Result;
import com.weichen.studentgameexchange.game.dao.*;
import com.weichen.studentgameexchange.game.entity.*;
import com.weichen.studentgameexchange.game.entity.vo.AddGameVo;
import com.weichen.studentgameexchange.game.entity.vo.SelectGameVo;
import com.weichen.studentgameexchange.game.entity.vo.UserGenreVo;
import com.weichen.studentgameexchange.game.entity.vo.UserSortVo;
import com.weichen.studentgameexchange.game.service.IGameService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 惟尘
 * @since 2025-02-01
 */
@Service
public class GameServiceImpl extends ServiceImpl<GameMapper, Game> implements IGameService {

    @Autowired
    GameMapper gameMapper;
    @Autowired
    GameDeveloperMapper gameDeveloperMapper;
    @Autowired
    GameGenreMapper gameGenreMapper;
    @Autowired
    GameSortMapper gameSortMapper;
    @Autowired
    GameVersionMapper gameVersionMapper;
    @Autowired
    UserGameCollectionMapper userGameCollectionMapper;
    @Autowired
    UserGameGradeMapper userGameGradeMapper;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    private double gradeCount = 0.0;

    /**
     * 新增游戏
     */
    @Override
    public Result adminAdd(AddGameVo addGameVo) {
        //判断游戏是否已存在数据库中
        Game one = Db.lambdaQuery(Game.class)
                .eq(addGameVo.getGameName() != null, Game::getGameName, addGameVo.getGameName())
                .one();
        if (!StrUtil.isBlankIfStr(one)) {
            return Result.error("游戏名已存在！");
        }
        //获取当前时间
        LocalDateTime localDateTime = LocalDateTime.now();
        addGameVo.setCreateTime(localDateTime);
        //1.将vo复制给game
        Game game = BeanUtil.copyProperties(addGameVo, Game.class);
        Db.save(game);
        Long gameId = game.getGameId();
        //2.将vo的游戏版本复制给gameversion
        GameVersion gameVersion = BeanUtil.copyProperties(addGameVo, GameVersion.class);
        gameVersion.setGameId(gameId);
        Db.save(gameVersion);
        //3.将vo的开发商复制给developer
        //3.1 前端传来的开发商以逗号分隔的,需要将逗号去掉，看看有几个开发商
        String developerName = addGameVo.getDeveloperName();
        List<String> developerNames = Arrays.stream(developerName.split(","))
                .map(String::trim)//对流中的每个字符串调用String.trim()方法，去除字符串两端的空白字符。
                .filter(name -> !name.isEmpty())
                .toList();
        //存储开发商id，以便插入游戏开发商表
        List<Long> ids = new ArrayList<>();
        //3.2 得到的开发商姓名集合检查是否和数据库的重复，重复不添加
        for (String str : developerNames) {
            String trim = str.trim();
            if (trim.isEmpty())
                continue;
            Developer developer = Db.lambdaQuery(Developer.class)
                    .eq(Developer::getDeveloperName, trim)
                    .one();
            //如果数据库中没有对应的开发商，则添加
            if (developer == null) {
                Developer newDeveloper = new Developer();
                newDeveloper.setDeveloperName(trim);
                Db.save(newDeveloper);
                ids.add(newDeveloper.getDeveloperId());
            } else {
                ids.add(developer.getDeveloperId());
            }
        }
        //3.3 将得到的开发商id插入游戏开发商表中
        for (Long id : ids) {
            GameDeveloper gameDeveloper = new GameDeveloper();
            gameDeveloper.setGameId(gameId);
            gameDeveloper.setDeveloperId(id);
            Db.save(gameDeveloper);
        }
        //4.将vo得到的游戏类型对应的类型id添加到gamegenre表中
        //存储游戏类型id，以便插入游戏-类型表
        List<Long> genreIds = new ArrayList<>();
        //4.1 通过类型名查找genre表找到对应的类型id
        List<String> genreNames = addGameVo.getGenreNames();
        for (String genreName : genreNames) {
            String trim = genreName.trim();
            Genre genre = Db.lambdaQuery(Genre.class)
                    .eq(Genre::getGenreName, trim)
                    .one();
            if (genre == null) {
                // 创建新类型
                Genre newGenre = new Genre();
                newGenre.setGenreName(trim);
                Db.save(newGenre);
                genreIds.add(newGenre.getGenreId());
            } else {
                genreIds.add(genre.getGenreId());
            }
        }
        //4.2 将得到的游戏类型id插入游戏-类型表中
        for (Long genreId : genreIds) {
            GameGenre gameGenre = new GameGenre();
            gameGenre.setGameId(gameId);
            gameGenre.setGenreId(genreId);
            Db.save(gameGenre);
        }
        return Result.success();
    }

    /**
     * 分页查询
     *
     * @param keyword
     * @param page
     * @param size
     * @return
     */
    @Override
    public Page<Game> adminPage(String keyword, Integer page, Integer size) {
        //1.创建分页参数
        Page<Game> gamePage = new Page<>(page, size);
        //2.构建查询条件：game_id倒叙
        LambdaQueryWrapper<Game> gameLambdaQueryWrapper = new LambdaQueryWrapper<Game>()
                .orderByDesc(Game::getGameId)
                .like(!StrUtil.isBlank(keyword), Game::getGameName, keyword);
//        //3.如果有搜索条件，根据搜索全模糊查询游戏名
//        if (!StrUtil.isBlankIfStr(keyword)){
//            gameLambdaQueryWrapper.like(Game::getGameName,keyword);
//        }
        //4.执行分页查询
        Page<Game> selectPage = gameMapper.selectPage(gamePage, gameLambdaQueryWrapper);
//在处理每个Game对象时，使用了Db.lambdaQuery来执行数据库查询。如果分页查询的结果很多，比如每页100条，那么每个Game对象都会触发两次查询，这可能导致大量的数据库查询，产生性能问题。
        //5.通过stream流处理，将两个总数放在page中
        List<Game> gameList = selectPage.getRecords().stream().map(game -> {
            Long gameId = game.getGameId();
            int collectionCount = Db.lambdaQuery(UserGameCollection.class)
                    .eq(gameId != null, UserGameCollection::getGameId, gameId)
                    .list()
                    .size();
            int gradeCount = Db.lambdaQuery(UserGameGrade.class)
                    .eq(gameId != null, UserGameGrade::getGameId, gameId)
                    .list()
                    .size();
            game.setCollectionCount(collectionCount);
            game.setGradeCount(gradeCount);
            return game;
        }).toList();
        selectPage.setRecords(gameList);
        return selectPage;
    }

    @Override
    @Transactional
    public Boolean adminDeleteOne(Long gameId) {
        gameDeveloperMapper.deleteByGameId(gameId);
        gameGenreMapper.deleteByGameId(gameId);
        gameSortMapper.deleteByGameId(gameId);
        //删除版本
        List<GameVersion> gameVersionList = Db.lambdaQuery(GameVersion.class)
                .eq(GameVersion::getGameId, gameId)
                .list();
        for (GameVersion gameVersion : gameVersionList) {
            gameVersionMapper.deleteById(gameVersion);
        }
//        userGameCollectionMapper.deleteByGameId(gameId);
//        userGameGradeMapper.deleteByGameId(gameId);
        gameMapper.deleteById(gameId);
        return true;
    }

    /**
     * 查看游戏名称
     *
     * @return
     */
    @Override
    public Result selectAllDeveloper() {
        List<Game> games = gameMapper.selectList(null);
        List<SelectGameVo> gameVos = new ArrayList<>();
        games.forEach(game -> {
            SelectGameVo gameVo = BeanUtil.copyProperties(game, SelectGameVo.class);
            gameVos.add(gameVo);
        });
        return Result.success(gameVos);
    }

    @Override
    public Result userSelectOne(Long gameId) {
        Game game = gameMapper.selectById(gameId);
        AddGameVo addGameVo = BeanUtil.copyProperties(game, AddGameVo.class);
        List<GameDeveloper> gameDevelopers = Db.lambdaQuery(GameDeveloper.class)
                .eq(GameDeveloper::getGameId, gameId)
                .list();
        if (gameDevelopers != null) {
            List<Long> developerIds = gameDevelopers.stream().map(GameDeveloper::getDeveloperId).toList();
            List<String> developerNames = Db.lambdaQuery(Developer.class)
                    .in(Developer::getDeveloperId, developerIds)
                    .list().stream().map(Developer::getDeveloperName).toList();
            String developerName = String.join(",", developerNames);
            addGameVo.setDeveloperName(developerName);
        }
        List<GameGenre> gameGenres = Db.lambdaQuery(GameGenre.class)
                .eq(GameGenre::getGameId, gameId)
                .list();
        if (gameGenres != null) {
            List<Long> genreIds = gameGenres.stream().map(GameGenre::getGenreId).toList();
            List<String> genreNames = Db.lambdaQuery(Genre.class)
                    .in(Genre::getGenreId, genreIds)
                    .list()
                    .stream()
                    .map(Genre::getGenreName)
                    .toList();
            addGameVo.setGenreNames(genreNames);
        }
        List<GameVersion> gameVersions = Db.lambdaQuery(GameVersion.class)
                .eq(GameVersion::getGameId, gameId)
                .list();
        if (gameVersions != null) {
            List<LocalDate> dates = gameVersions.stream().map(GameVersion::getReleaseDate).toList();
            LocalDate date = Collections.max(dates);
            addGameVo.setReleaseDate(date);
            String versionNumber = Db.lambdaQuery(GameVersion.class)
                    .eq(GameVersion::getReleaseDate, date)
                    .one()
                    .getVersionNumber();
            addGameVo.setVersionNumber(versionNumber);
        }
        return Result.success(addGameVo);
    }

    @Override
    public Game getGameId(String gameName) {
        Game game = Db.lambdaQuery(Game.class)
                .eq(Game::getGameName, gameName)
                .one();
        return game;
    }

    @Override
    public List<Game> getGamesByIds(List<Long> gameIds) {
        List<Game> games = new ArrayList<>();
        gameIds.forEach(gameId -> {
            Game game = gameMapper.selectById(gameId);
            games.add(game);
        });
        return games;
    }

    @Override
    public List<Game> getLikeGame(String gameName) {
        LambdaQueryWrapper<Game> gameLambdaQueryWrapper = new LambdaQueryWrapper<>();
        gameLambdaQueryWrapper.like(Game::getGameName, gameName);
        return gameMapper.selectList(gameLambdaQueryWrapper);
    }

    @Override
    public Result userPage(String gameName, Integer page, Integer size) {
        Page<Game> gamePage = new Page<>(page, size);
        LambdaQueryWrapper<Game> gameLambdaQueryWrapper = new LambdaQueryWrapper<>();
        gameLambdaQueryWrapper.orderByDesc(Game::getGameId);
        if (!StrUtil.isBlankIfStr(gameName))
            gameLambdaQueryWrapper.like(Game::getGameName, gameName);
        Page<Game> selectPage = gameMapper.selectPage(gamePage, gameLambdaQueryWrapper);
        List<Game> records = selectPage.getRecords();
        List<UserGenreVo> vos = new ArrayList<>();
        records.forEach(game -> {
            gradeCount = 0.0;
            UserGenreVo vo = BeanUtil.copyProperties(game, UserGenreVo.class);
            List<Long> genreIds = Db.lambdaQuery(GameGenre.class)
                    .eq(GameGenre::getGameId, game.getGameId())
                    .list()
                    .stream()
                    .map(GameGenre::getGenreId)
                    .toList();
            List<String> genreNames = Db.lambdaQuery(Genre.class)
                    .in(Genre::getGenreId, genreIds)
                    .list()
                    .stream()
                    .map(Genre::getGenreName)
                    .toList();
            vo.setGenreName(genreNames.get(0));
            int count = Db.lambdaQuery(UserGameCollection.class)
                    .eq(UserGameCollection::getGameId, game.getGameId())
                    .list().size();
            vo.setCollectionCount(count);
            List<UserGameGrade> list = Db.lambdaQuery(UserGameGrade.class)
                    .eq(UserGameGrade::getGameId, game.getGameId())
                    .list();
            if (list.size() > 0) {
                List<BigDecimal> bigDecimals = list.stream().map(UserGameGrade::getGradeScore).toList();
                bigDecimals.forEach(bigDecimal -> {
                    double v = bigDecimal.doubleValue();
                    gradeCount = gradeCount + v;
                });
                gradeCount = gradeCount / (list.size());
            }
            vo.setGradeCount(gradeCount);
            vos.add(vo);
        });
        Page<UserGenreVo> userGenreVoPage = new Page<>();
        userGenreVoPage.setCurrent(selectPage.getCurrent());
        userGenreVoPage.setSize(selectPage.getSize());
        userGenreVoPage.setTotal(selectPage.getTotal());
        userGenreVoPage.setRecords(vos);
        return Result.success(userGenreVoPage);
    }

    @Override
    public Result userSortPage(String gameName, Integer page, Integer size) {
        Page<Game> gamePage = new Page<>(page, size);
        LambdaQueryWrapper<Game> gameLambdaQueryWrapper = new LambdaQueryWrapper<>();
        gameLambdaQueryWrapper.orderByDesc(Game::getGameId);
        if (!StrUtil.isBlankIfStr(gameName))
            gameLambdaQueryWrapper.like(Game::getGameName, gameName);
        Page<Game> selectPage = gameMapper.selectPage(gamePage, gameLambdaQueryWrapper);
        List<Game> records = selectPage.getRecords();
        List<UserSortVo> vos = new ArrayList<>();
        records.forEach(game -> {
            gradeCount = 0.0;
            UserSortVo vo = BeanUtil.copyProperties(game, UserSortVo.class);
            int count = Db.lambdaQuery(UserGameCollection.class)
                    .eq(UserGameCollection::getGameId, game.getGameId())
                    .list().size();
            vo.setCollectionCount(count);
            List<UserGameGrade> list = Db.lambdaQuery(UserGameGrade.class)
                    .eq(UserGameGrade::getGameId, game.getGameId())
                    .list();
            if (list.size() > 0) {
                List<BigDecimal> bigDecimals = list.stream().map(UserGameGrade::getGradeScore).toList();
                bigDecimals.forEach(bigDecimal -> {
                    double v = bigDecimal.doubleValue();
                    gradeCount = gradeCount + v;
                });
                gradeCount = gradeCount / (list.size());
            }
            vo.setGradeCount(gradeCount);
            vos.add(vo);
        });
        Page<UserSortVo> userSortVoPage = new Page<>();
        userSortVoPage.setCurrent(selectPage.getCurrent());
        userSortVoPage.setSize(selectPage.getSize());
        userSortVoPage.setTotal(selectPage.getTotal());
        userSortVoPage.setRecords(vos);
        return Result.success(userSortVoPage);
    }
}
